/*
 * Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.huawei.cloudphone.virtualdevice.camera;

import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_CAMERA;
import static com.huawei.cloudphone.jniwrapper.JNIWrapper.CAMERA_DATA;
import static com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol.MSG_HEADER_LEN;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import androidx.core.app.ActivityCompat;

import com.huawei.cloudphone.api.CloudPhonePermissionInfo;
import com.huawei.cloudphone.api.CloudPhonePermissionRequestListener;
import com.huawei.cloudphone.virtualdevice.common.IVirtualDeviceDataListener;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol;
import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol.MsgHeader;

public class VirtualCameraManager extends VirtualDeviceManager {
    private static final String TAG = "VirtualCameraManager";

    private static final short OPT_CAMERA_GET_PARAM_REQ = 0x1;
    private static final short OPT_CAMERA_GET_PARAM_RSP = 0x1001;
    private static final short OPT_CAMERA_SET_PARAM_REQ = 0x2;
    private static final short OPT_CAMERA_SET_PARAM_RSP = 0x1002;
    private static final short OPT_CAMERA_GET_INFO_REQ = 0x3;
    private static final short OPT_CAMERA_GET_INFO_RSP = 0x1003;
    private static final short OPT_CAMERA_START_PREVIEW_REQ = 0x4;
    private static final short OPT_CAMERA_START_PREVIEW_RSP = 0x1004;
    private static final short OPT_CAMERA_STOP_PREVIEW_REQ = 0x5;
    private static final short OPT_CAMERA_STOP_PREVIEW_RSP = 0x1005;
    private static final short OPT_CAMERA_IS_SUPPORT_H264_REQ = 0x6;
    private static final short OPT_CAMERA_IS_SUPPORT_H264_RSP = 0x1006;
    private static final short OPT_CAMERA_GET_SIZE_REQ = 0x7;
    private static final short OPT_CAMERA_GET_SIZE_RSP = 0x1007;
    private static final short OPT_CAMERA_FRAME = 0x8;
    private static final short OPT_CAMERA_INVALID = 0x9;

    private static final int RSP_RESULT_LENGTH = 2;
    private static final int RSP_INFO_LENGTH = 3;
    private static final int RSP_SIZE_LENGTH = 4;
    private static final int RSP_SUPPORT_H264_LENGTH = 1;

    private VirtualCamera mVirtualCamera;
    private VirtualDeviceProtocol mVirtualDeviceProtocol;
    private Context mContext;
    private CloudPhonePermissionRequestListener mPermissionListener;
    private int mWidth;
    private int mHeight;
    private int mFrameRate;
    private int mDevId;

    public VirtualCameraManager(VirtualDeviceProtocol virtualDeviceProtocol, Context context) {
        mVirtualCamera = new VirtualCamera();
        mVirtualDeviceProtocol = virtualDeviceProtocol;
        mContext = context;
    }

    public void stop() {
        mVirtualCamera.stopPreview();
    }

    @Override
    public void setPermissionListener(CloudPhonePermissionRequestListener listener) {
        mPermissionListener = listener;
    }

    public void updateDynamicBitrate(int bitrate) {
        mVirtualCamera.updateDynamicBitrate(bitrate);
    }

    public int getBitrate() {
        return mVirtualCamera.getBitrate();
    }

    public void processMsg(MsgHeader header, byte[] body) {
        switch (header.mOptType) {
            case OPT_CAMERA_GET_PARAM_REQ:
                Log.i(TAG, "processMsg: get param.");
                handleGetParamReq(header, body);
                break;
            case OPT_CAMERA_SET_PARAM_REQ:
                Log.i(TAG, "processMsg: set param.");
                handleSetParamReq(header, body);
                break;
            case OPT_CAMERA_GET_INFO_REQ:
                Log.i(TAG, "processMsg: get info.");
                handleGetInfoReq(header, body);
                break;
            case OPT_CAMERA_START_PREVIEW_REQ:
                Log.i(TAG, "processMsg: start preview.");
                handleStartPreviewReq(header, body);
                break;
            case OPT_CAMERA_STOP_PREVIEW_REQ:
                Log.i(TAG, "processMsg: stop preview.");
                handleStopPreviewReq(header, body);
                break;
            case OPT_CAMERA_IS_SUPPORT_H264_REQ:
                Log.i(TAG, "processMsg: is support h264.");
                handleIsSupportH264Req(header, body);
                break;
            case OPT_CAMERA_GET_SIZE_REQ:
                Log.i(TAG, "processMsg: get size.");
                handleGetSizeReq(header, body);
                break;
            default:
                Log.e(TAG, "processMsg: Invalid msg type");
        }
    }

    private void handleGetParamReq(MsgHeader header, byte[] body) {
        int result = mVirtualCamera.open(header.mDeviceId);
        byte[] param = mVirtualCamera.getParameters();
        byte[] rspBody = new byte[param.length + RSP_RESULT_LENGTH];
        rspBody[0] = 0x0;
        rspBody[1] = (byte) (result == 0 ? 0x0 : 0x1);
        System.arraycopy(param, 0, rspBody, RSP_RESULT_LENGTH, param.length);
        int rspMsgLen = MSG_HEADER_LEN + RSP_RESULT_LENGTH + param.length;
        MsgHeader rspHeader = new MsgHeader(OPT_CAMERA_GET_PARAM_RSP, DEV_TYPE_CAMERA, header.mDeviceId, rspMsgLen);
        mVirtualDeviceProtocol.sendMsg(rspHeader, rspBody, CAMERA_DATA);
    }

    private void handleSetParamReq(MsgHeader header, byte[] body) {
        mVirtualCamera.setParameters(body);
        byte[] rspBody = new byte[RSP_RESULT_LENGTH];
        int rspMsgLen = MSG_HEADER_LEN + RSP_RESULT_LENGTH;
        rspBody[0] = 0x0;
        rspBody[1] = 0x0;
        MsgHeader rspHeader = new MsgHeader(OPT_CAMERA_SET_PARAM_RSP, DEV_TYPE_CAMERA, header.mDeviceId, rspMsgLen);
        mVirtualDeviceProtocol.sendMsg(rspHeader, rspBody, CAMERA_DATA);
    }

    private void handleGetInfoReq(MsgHeader header, byte[] body) {
        int face = mVirtualCamera.getFacing();
        int orientation = mVirtualCamera.getOrientation();
        byte[] rspBody = new byte[RSP_RESULT_LENGTH + RSP_INFO_LENGTH];
        rspBody[0] = 0x0;
        rspBody[1] = 0x0;
        rspBody[2] = (byte) (face & 0x00FF);
        rspBody[3] = (byte) (orientation >> 8);
        rspBody[4] = (byte) (orientation & 0x00FF);
        int rspMsgLen = MSG_HEADER_LEN + RSP_RESULT_LENGTH + RSP_INFO_LENGTH;
        MsgHeader rspHeader = new MsgHeader(OPT_CAMERA_GET_INFO_RSP, DEV_TYPE_CAMERA, header.mDeviceId, rspMsgLen);
        mVirtualDeviceProtocol.sendMsg(rspHeader, rspBody, CAMERA_DATA);
    }

    public void init() {
        if (mVirtualCamera.open(mDevId) != 0) {
            Log.e(TAG, "initCamera: Failed to open camera");
            return;
        }
        mVirtualCamera.setOnRecvData(new CameraDataListener());
        mVirtualCamera.setResolution(mWidth, mHeight);
        mVirtualCamera.setFps(mFrameRate);
        if (mVirtualCamera.startPreview() != 0) {
            Log.e(TAG, "initCamera: Failed to start preview");
        }
    }

    private void handleStartPreviewReq(MsgHeader header, byte[] body) {
        mWidth = (short)((body[0] << 8) | (body[1] & 0x00FF));
        mHeight = (short)((body[2] << 8) | (body[3] & 0x00FF));
        mFrameRate = (body[4] << 8) | (body[5] & 0x00FF);
        mDevId = header.mDeviceId;
        Context context = mContext.getApplicationContext();
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
        if (context.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            if (mPermissionListener == null) {
                ActivityCompat.requestPermissions((Activity) mContext,
                        new String[]{Manifest.permission.CAMERA},
                        DEV_TYPE_CAMERA);
            } else {
                mPermissionListener.onRequestPermissions(new CloudPhonePermissionInfo(
                        DEV_TYPE_CAMERA, new String[]{Manifest.permission.CAMERA}));
            }
        } else {
            init();
        }
    }

    private void handleStopPreviewReq(MsgHeader header, byte[] body) {
        mVirtualCamera.setOnRecvData(null);
        mVirtualCamera.stopPreview();
        byte[] rspBody = new byte[RSP_RESULT_LENGTH];
        int rspMsgLen = MSG_HEADER_LEN + RSP_RESULT_LENGTH;
        MsgHeader rspHeader = new MsgHeader(OPT_CAMERA_STOP_PREVIEW_RSP, DEV_TYPE_CAMERA, header.mDeviceId, rspMsgLen);
        mVirtualDeviceProtocol.sendMsg(rspHeader, rspBody, CAMERA_DATA);
    }

    private void handleIsSupportH264Req(MsgHeader header, byte[] body) {
        boolean isSupportH264 = mVirtualCamera.isSupportH264();
        byte[] rspBody = new byte[RSP_RESULT_LENGTH + RSP_SUPPORT_H264_LENGTH];
        rspBody[2] = (byte) ((isSupportH264 ? 1 : 0) & 0x00FF);
        int rspMsgLen = MSG_HEADER_LEN + RSP_SUPPORT_H264_LENGTH;
        MsgHeader rspHeader = new MsgHeader(OPT_CAMERA_IS_SUPPORT_H264_RSP, DEV_TYPE_CAMERA, header.mDeviceId, rspMsgLen);
        mVirtualDeviceProtocol.sendMsg(rspHeader, rspBody, CAMERA_DATA);
    }

    private void handleGetSizeReq(MsgHeader header, byte[] body) {
        int width = -1;
        int height = -1;

        byte[] rspBody = new byte[RSP_RESULT_LENGTH + RSP_SIZE_LENGTH];
        rspBody[0] = 0x0;
        rspBody[1] = 0x0;
        rspBody[2] = (byte) (width >> 8);
        rspBody[3] = (byte) (width & 0x00FF);
        rspBody[4] = (byte) (height >> 8);
        rspBody[5] = (byte) (height & 0x00FF);
        int rspMsgLen = MSG_HEADER_LEN + RSP_RESULT_LENGTH + RSP_SIZE_LENGTH;
        MsgHeader rspHeader = new MsgHeader(OPT_CAMERA_GET_SIZE_RSP, DEV_TYPE_CAMERA, header.mDeviceId, rspMsgLen);
        mVirtualDeviceProtocol.sendMsg(rspHeader, rspBody, CAMERA_DATA);
    }

    public void generateKeyFrame() {
        if (mVirtualCamera != null) {
            mVirtualCamera.generateKeyFrame();
        }
    }

    class CameraDataListener implements IVirtualDeviceDataListener {
        @Override
        public void onRecvData(Object... args) {
            byte[] data = (byte[]) args[0];
            int length = (int) args[1];
            int devId = (int) args[2];
            int repMsgLen = length + MSG_HEADER_LEN;
            MsgHeader header = new MsgHeader(OPT_CAMERA_FRAME, DEV_TYPE_CAMERA, (short) devId, repMsgLen);
            byte[] repBody = new byte[length];
            System.arraycopy(data, 0, repBody, 0, length);
            mVirtualDeviceProtocol.sendMsg(header, repBody, CAMERA_DATA);
        }
    }

}
